Revision:
<header-component></header-component>
<script>
const headerTemplate = document.createElement('template');
headerTemplate.innerHTML = `
<style>
.header{text-align: center;}
h3{color: blue; font-size: 1.5vw;}
</style>
<div>
<div class="header">
<h3> Header - my initial reflections with respect to web components </h3>
</div>
</div>`
class Header extends HTMLElement {
constructor() {
super();
}
connectedCallback(){
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(headerTemplate.content);
}
}
customElements.define('header-component', Header);
</script>
<my-component></my-component>
<script>
class MyComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({mode: 'open'}).innerHTML = `
<style>
span {color: darkblue;}
</style>
<span>The color of this sentence should be darkblue</span>
`
}
}
customElements.define('my-component', MyComponent)
</script>
<div>
<first-component></first-component>
<second-component></second-component><br><br>
</div>
<script>
class FirstComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
:host{--background: lightgreen;}
span{background: var(--background);}
</style>
<span>Hi, there. This is the first component!</span><br><br>
`
}
}
class SecondComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
:host{--background: lightblue;}
span{background: var(--background);}
</style>
<span>Hello, this is the second component!</span>
`
}
}
customElements.define('first-component', FirstComponent)
customElements.define('second-component', SecondComponent)
</script>
<div>
<first-component-a></first-component-a><br><br>
<second-component-a></second-component-a>
</div>
<script>
class FirstComponentA extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
:host{--background: lightgreen;}
span{background: var(--background);}
</style>
<span>Hello, this is the "FIRST" component!</span>
`
}
}
class SecondComponentA extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
:host{--background: lightblue;}
span{background: var(--background);}
</style>
<span>Hello, this is the "SECOND" component!</span>
`
}
}
customElements.define('first-component-a', FirstComponentA)
customElements.define('second-component-a', SecondComponentA)
</script>
<div>
<div>Default:
<custom-component></custom-component>
</div><br>
<div>Dark:
<custom-component class="dark"></custom-component>
</div><br>
<div>Light:
<custom-component class="light"></custom-component>
</div><br>
</div>
<script>
class CustomComponent extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: 'open' }).innerHTML = `
<style>
:host, :host(.light) {background: white; color: black;}
:host(.dark) {background: black; color: white;}
@media (prefers-color-scheme: dark) {
:host {background: black; color: white;}
}
</style>
<span>Hello world!</span>
`
}
}
customElements.define('custom-component', CustomComponent)
</script>
<div>
<label-uppercase value="Welcome to Web Components"></label-uppercase><br><br>
</div>
<script>
class UpperCaseLabel extends HTMLElement {
get value() {
return this.getAttribute("value");
}
set value(newValue) {
this.setAttribute("value", newValue);
}
connectedCallback() {
let stringContent = (this.value && null !== this.value) ? this.value : "";
this.innerHTML = `<label>${stringContent.toUpperCase()}</label>`;
}
}
customElements.define("label-uppercase", UpperCaseLabel);
</script>
<div>
<template id="my-drop-down">
<style>
.menu-wrap{width: 20.8334vw; display: block; background-color: transparent; font-family: 'ExtraLight'; user-select: none;
margin: 20px 40px;}
.menu-label{font-size: 1vw; color: rgba(227, 141, 39, .8);padding: 1% 0% 2.5% 7%;
}
.menu-head{border: 1px solid #e38d27;}
.menu-head, .menu-body {background-color: rgba(0, 0, 0, .6);}
.menu-head, ::slotted(.menu-option) {min-height: 2.2vw;}
.current-choice{color: #fff; height: 2.2vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
padding: .7vw .1vw .1vw 1.7vw;}
.current-choice, ::slotted(.menu-option){text-transform: capitalize; font-size: 1.2vw;}
.menu-body{max-height: 13vw; overflow-y: scroll; overflow-x: hidden;}
::slotted(.menu-option) {color: rgba(255, 255, 255, .3); position: relative; padding-left: 1.7vw; display: flex;
align-items: center; transition: padding .5s linear, background-color .25s linear, color .25s linear;}
::slotted(.menu-option::after){content: ''; width: 100%; height: 1px;background-color: rgba(255, 255, 255, .15);
position: absolute; bottom: 0; left: 0; }
::slotted(.menu-option:hover){background-color: #e38d27; color: #fff;padding-left: 12%;}
.menu-body::-webkit-scrollbar {width: 2.292vw;}
.menu-body::-webkit-scrollbar-thumb {background-color: #e38d27; border: .78125vw solid rgba(0, 0, 0, 0);
background-clip: padding-box;}
.menu-body::-webkit-scrollbar-track {background-color: #111; border: .78125vw solid black;}
</style>
<div id="my-menu" class="menu-wrap">
<div class="menu-label">Choose your language</div>
<div class="menu-head"><div class="current-choice">English</div></div>
<div id="menu-body" class="menu-body">
<slot></slot>
</div>
</div>
</template>
<drop-down>
<div class="menu-option">English</div>
<div class="menu-option">Deutsch</div>
<div class="menu-option">Espanol</div>
<div class="menu-option">Magyar</div>
<div class="menu-option">Dansk</div>
<div class="menu-option">Euskara</div>
<div class="menu-option">Hrvatski</div>
<div class="menu-option">Italiano</div>
</drop-down>
<drop-down>
<div class="menu-option">English</div>
<div class="menu-option">Deutsch</div>
</drop-down>
<drop-down>
<div class="menu-option">English</div>
<div class="menu-option">Deutsch</div>
<div class="menu-option">Espanol</div>
<div class="menu-option">Magyar</div>
<div class="menu-option">Dansk</div>
<div class="menu-option">Euskara</div>
<div class="menu-option">Hrvatski</div>
<div class="menu-option">Italiano</div>
<div class="menu-option">English</div>
<div class="menu-option">Deutsch</div>
<div class="menu-option">Espanol</div>
<div class="menu-option">Magyar</div>
<div class="menu-option">Dansk</div>
<div class="menu-option">Euskara</div>
<div class="menu-option">Hrvatski</div>
<div class="menu-option">Italiano</div>
<div class="menu-option">English</div>
<div class="menu-option">Deutsch</div>
</drop-down>
</div>
<script>
class DropDown extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
// init the component
let shadowDOM = this.attachShadow({ mode: "open" });
let template = document.getElementById("my-drop-down");
let templateHtml = template.content.cloneNode(true);
shadowDOM.appendChild(templateHtml);
// define the menu, menu body and hide it
let menu = shadowDOM.getElementById("my-menu");
let menuHead = menu.children[1];
let menuBody = menu.children[2];
menuBody.style.display = "none";
// toggle the menu body
function toggleMenu() {
menuHead.addEventListener("click", function() {
if (menuBody.style.display === "none") {
menuBody.style.display = "block";
} else {
menuBody.style.display = "none";
}
});
menuBody.addEventListener("click", function(ev) {
if (ev.target !== ev.currentTarget) {
menuHead.children[0].textContent = ev.target.textContent;
}
menuBody.style.display = "none";
});
}
// init toggle listeners
toggleMenu();
}
}
// init the element
customElements.define("drop-down", DropDown);
</script>
<my-counter></my-counter>
<script>
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = 0;
const style = `
*{font-size: 200%;}
span{width: 4rem; display: inline-block; text-align: center;}
button{width: 64px; height: 64px; border: none; border-radius: 10px; background-color: seagreen; color: white;}
`;
const html = `
<button id="dec">-</button>
<span>${this.count}</span>
<button id="inc">+</button>
`;
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
${style}
</style>
${html}
`;
this.buttonInc = this.shadowRoot.getElementById('inc');
this.buttonDec = this.shadowRoot.getElementById('dec');
this.spanValue = this.shadowRoot.querySelector('span');
this.inc = this.inc.bind(this);
this.dec = this.dec.bind(this);
}
inc() {
this.count++;
this.update();
}
dec() {
this.count--;
this.update();
}
update() {
this.spanValue.innerText = this.count;
}
connectedCallback() {
this.buttonInc.addEventListener('click', this.inc);
this.buttonDec.addEventListener('click', this.dec);
}
disconnectedCallback() {
this.buttonInc.removeEventListener('click', this.inc);
this.buttonDec.removeEventListener('click', this.dec);
}
}
customElements.define('my-counter', MyCounter);
</script>
</pre>
0 seconds
Lonely, unstyled paragraph.
<div>
<template id="cool-timer">
<style>
p.timer-display { display: block; padding: 2w; border-radius: 0.5vw; font-size: 3vw; border: 1px solid #212121;
color: #212121; background: #fff; }
p {background: #eb6; color: #212121; padding: 0.5vw; }
</style>
<p class="timer-display"><span id="timer">0</span> seconds</p>
<p>
<!-- A custom description that can be overridden in markup -->
<slot name="timer-description">A cool timer worth the attention!</slot>
</p>
</template>
<!-- Actual, rendered instance of the cool timer -->
<cool-timer><span slot="timer-description">Lap 1 time</span></cool-timer>
<p>Lonely, unstyled paragraph.</p>
</div>
<script>
class CoolTimer extends HTMLElement {
constructor(){
super();
const template = document.getElementById("cool-timer");
const templateContent = template.content;
const shadowRoot = this.attachShadow({ mode: "open" }).appendChild( templateContent.cloneNode(true));
}
// Called when the element is first connected to the DOM
connectedCallback() {
const timerDisplay = this.shadowRoot.getElementById("timer");
let elapsedSeconds = 0;
// Every second, increment elapsed seconds and update timer display
this.timer = setInterval(() => {
elapsedSeconds++;
timerDisplay.innerHTML = elapsedSeconds;
}, 1000);
}
// Called when custom element is removed
disconnectedCallback() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
}
}
customElements.define("cool-timer", CoolTimer);
</script>